#include "VideoFileStream.h"
#include "mediaPriv.h"
#include "VideoDecode.h"

extern "C" unsigned long long vdo_get_time();

namespace Media{

std::map<int, VideoFileStream *> VideoFileStream::sm_streamMap;
std::recursive_mutex 		     VideoFileStream::sm_mapMutex;

VideoFileStream *VideoFileStream::instance(int channel)
{
    std::lock_guard<std::recursive_mutex> guard(sm_mapMutex);
    
	if(sm_streamMap.find(channel) == sm_streamMap.end())
	{
		sm_streamMap[channel] = new VideoFileStream(channel);
	}

	return sm_streamMap[channel];
}


VideoFileStream::VideoFileStream(int channel) 
: m_videoChannel(-1)
, m_started(false)
, m_fileHd(MP4_INVALID_FILE_HANDLE)
, m_videoTrackId(MP4_INVALID_TRACK_ID)
, m_audioTrackId(MP4_INVALID_TRACK_ID)
, m_audioChannel(-1)
{   
    m_taskTimer.reset();
}

bool VideoFileStream::setFormat(VideoDecFormat *format)
{
    if(!format || !m_pVideoDecode)
	{
	    errorf("format or m_videoDecoder is NULL \n");
		return false;
	}
		
    return m_pVideoDecode->setFormat(format);
}

bool VideoFileStream::timerTaskCb()
{
    bool ret = streamStart(m_fileStreamName);
    if(!ret)
    {
        return false;
    }

    if((m_playAbility == VIDEO_AUDIO) || (m_playAbility == VIDEO_ONLY))
    {
        m_videoThread.start([this](Fdt::Thread *thread){this->VideoThreadProc(thread);}); //启动单独线程处理视频流
        //m_videoThread.start([this](Fdt::Thread *thread){this->ThreadProc(thread);}); 
    }

    if((m_playAbility == VIDEO_AUDIO) || (m_playAbility == AUDIO_ONLY))
    {
        m_audioThread.start([this](Fdt::Thread *thread){this->AudioThreadProc(thread);}); //启动单独线程处理音频流
    }
    
    if(m_taskInfo.StopTime != -1)
    {
        m_taskTimer.reset(new Fdt::Timer(m_taskInfo.StopTime * 1000, ([this](){return this->stop();}), nullptr)); //不是循环播放，注册停止函数
    }
    else
    {
        m_taskTimer.reset();
    }

    return true;
}


bool VideoFileStream::start(StreamPlayInfo playInfo)
{
    LOG_PrintWarn("fileName=%s, StartTime=%d, StopTime=%d\n",playInfo.stream.content,playInfo.task.StartTime,playInfo.task.StopTime);
	
    if(!m_started)
    {   
        m_fileStreamName = videoPath + playInfo.stream.content;

        m_playAbility = playInfo.playAbility;

    	///创建视频解码并启动
        if((m_playAbility == VIDEO_AUDIO) || (m_playAbility == VIDEO_ONLY))
        {
            if(m_videoChannel != playInfo.videoChn)
            {            
                m_videoChannel = playInfo.videoChn;
                m_pVideoDecode = VideoDecode::instance(m_videoChannel); 
            }
            if(!m_pVideoDecode)
            {
                return false;
            }
        }

        ///创建音频输入管理对象
        if((m_playAbility == VIDEO_AUDIO) || (m_playAbility == AUDIO_ONLY))
        {
            if(m_audioChannel != playInfo.audioChn)
            {            
                m_audioChannel = playInfo.audioChn;
                m_pAudioDecode = AudioDecode::instance(m_audioChannel);
            }
            if(!m_pAudioDecode)
            {
                return false;
            }
        }

    	m_taskInfo = playInfo.task;

    	m_taskTimer.reset(new Fdt::Timer(m_taskInfo.StartTime * 1000, ([this](){return this->timerTaskCb();}), nullptr));

    	m_started = true;
    }

    //LOG_PrintWarn("start leave \n");
  
	return true;
}

bool VideoFileStream::stop()
{
    //tracef("stop enter \n");
	if(m_started)
    {
        m_started = false;

        m_taskTimer.reset();
        
        streamStop();

        m_videoThread.stop();

        m_audioThread.stop();

        //usleep(500*1000);

        if((m_playAbility == VIDEO_AUDIO) || (m_playAbility == VIDEO_ONLY))
        {
            if (!m_pVideoDecode || !m_pVideoDecode->stop())
        	{
        		return false;
        	}
    	}

    	if((m_playAbility == VIDEO_AUDIO) || (m_playAbility == AUDIO_ONLY))
        {
            if (!m_pAudioDecode || !m_pAudioDecode->stop())
        	{
        		return false;
        	}
    	}
    }
    //tracef("stop leave \n");
   
	return true;
}

bool VideoFileStream::streamStart(std::string fileName)
{    
    std::lock_guard<std::mutex> guard(m_fileMutex);
    
    /*// 设置MP4调试级别 => 最高最详细级别...
	MP4LogLevel theLevel = MP4LogGetLevel();
	MP4LogSetLevel(MP4_LOG_VERBOSE4);*/
	
    m_fileHd = MP4Read(fileName.c_str());
    if(!m_fileHd)
    {
        errorf("MP4Read failed,fileName=%s\n", fileName.c_str());
        return false;
    }

    return parseMP4HeadInfo(m_fileHd);
}

	
bool VideoFileStream::streamStop(void)
{
    std::lock_guard<std::mutex> guard(m_fileMutex);
    if(m_fileHd)
    {
        m_videoTrackId = MP4_INVALID_TRACK_ID;
        m_audioTrackId = MP4_INVALID_TRACK_ID;
        MP4Close(m_fileHd, 0);        
        
        m_fileHd = MP4_INVALID_FILE_HANDLE;      
    }
    
    return true;
}

bool VideoFileStream::sendVideoPacket(unsigned char *buffer, int buflen, uint64_t timestamp)
{
    if(m_pVideoDecode)
    {
        m_videoPacket.buffer = buffer;
        
        m_videoPacket.length = buflen;
        m_videoPacket.pts = timestamp;


        //video_FILE_Write("/nfsroot/StreamFile/VideoFile/111.h264", m_videoPacket.buffer, m_videoPacket.length);

        return m_pVideoDecode->putPacket(&m_videoPacket);
    }
    return false;
}

bool VideoFileStream::parseMP4HeadInfo(MP4FileHandle inFile)
{
	if( inFile == MP4_INVALID_FILE_HANDLE )
		return false;

	// 首先获取文件的每秒刻度数和总刻度数(不是毫秒数)...
	uint32_t dwFileScale = MP4GetTimeScale(inFile);
	MP4Duration theDuration = MP4GetDuration(inFile);
	printf("=== File second dwFileScale%u ===\n", dwFileScale);
	printf("=== File total Duration：%llu ===\n", theDuration);
	// 总毫秒数 = 总刻度数*1000/每秒刻度数 => 先乘法可以降低误差...
	uint32_t		dwMP4Duration = theDuration*1000/dwFileScale;
	printf("===dwMP4Duration=> %u ===\n", dwMP4Duration); //总毫秒数 = 文件总刻度数*1000/文件每秒刻度数
	MP4TrackId		tidVideo = MP4_INVALID_TRACK_ID;	// 视频轨道编号
	MP4TrackId		tidAudio = MP4_INVALID_TRACK_ID;	// 音频轨道编号
	int				audio_rate_index = 0;
	int				audio_channel_num = 0;
	int				audio_type = 0;
	uint32_t		audio_time_scale = 0;
	uint32_t		video_time_scale = 0;
	char nalStartCode[4] = {0x00,0x00,0x00,0x01};
	int  startCodeLen = 0;
	uint8_t sendFlag = 0;
	uint8_t lpVPS[64] = {0};
	uint8_t lpSPS[64] = {0};
	uint8_t lpPPS[64] = {0};
	uint32_t nSize = 0;
	bool bResult = false;
	
	// 获取需要的相关信息...
    uint32_t trackCount = MP4GetNumberOfTracks(inFile);
	printf("===trackCount:%u ===\n", trackCount);
    for( uint32_t i = 0; i < trackCount; ++i) 
    {
		MP4TrackId  id = MP4FindTrackId( inFile, i);
		const char* type = MP4GetTrackType( inFile, id);
		if( MP4_IS_VIDEO_TRACK_TYPE(type) 
		    && ((m_playAbility == VIDEO_AUDIO) || (m_playAbility == VIDEO_ONLY))) 
		{			
			if( tidVideo > 0 )
				continue; // 视频已经有效，检测下一个...
			// 获取视频信息...
			tidVideo = id;
			printf("===tidVideo:%d, type:%s ===\n", tidVideo, type);
			if(tidVideo == MP4_INVALID_TRACK_ID)
			{
                continue;
			}
			
			char * lpVideoInfo = MP4Info(inFile, id);
			printf("lpVideoInfo:%s \n", lpVideoInfo);
			free(lpVideoInfo);
			// 获取视频时间间隔...
			video_time_scale = MP4GetTrackTimeScale(inFile, id);
			printf("===video_time_scale:%u ===\n", video_time_scale);
			const char * video_name = MP4GetTrackMediaDataName(inFile, id);
			printf("=== video_encode:%s===\n", video_name);
			// 获取视频的VPS/PPS/SPS信息...
			uint8_t  ** vpsHeader = NULL;
			uint8_t  ** spsHeader = NULL;
			uint8_t  ** ppsHeader = NULL;
			uint32_t  * vpsSize = NULL;
			uint32_t  * spsSize = NULL;
			uint32_t  * ppsSize = NULL;
			uint32_t    ix = 0;

			VideoDecFormat decFormat;
            memset(&decFormat, 0, sizeof(VideoDecFormat));
            
#if 1		
            decFormat.type = videoEncH264;
			bResult = MP4GetTrackH264SeqPictHeaders(inFile, id, &spsHeader, &spsSize, &ppsHeader, &ppsSize); //H264
			if(!bResult)
            {
                errorf("MP4GetTrackH264SeqPictHeaders failed\n");
                return false;
            }
#else
            decFormat.type = videoEncH265;
            
			bResult = MP4GetTrackH265SeqPictHeaders(inFile, id, &vpsHeader, &vpsSize, &spsHeader, &spsSize, &ppsHeader, &ppsSize);//H265
			if(!bResult)
            {
                errorf("MP4GetTrackH265SeqPictHeaders failed\n");
                return false;
            }

#endif
            if((!m_pVideoDecode) || (!m_pVideoDecode->setFormat(&decFormat)))
            {
                errorf("VideoDecode setFormat error\n");
                return false;
            }
            
			if((!m_pVideoDecode) || (!m_pVideoDecode->start()))
            {
                errorf("VideoDecode start error\n");
                return false;
            }

            /*
			///处理VPS信息
			sendFlag = 0;
			for(ix = 0; vpsSize[ix] != 0; ++ix) 
			{
			    if(sendFlag == 0) // 只发送第一个VPS信息...
			    {
			        startCodeLen = sizeof(nalStartCode);
    				nSize = vpsSize[ix];

    				memcpy(lpSPS, nalStartCode, startCodeLen);
    				memcpy(&lpSPS[startCodeLen], vpsHeader[ix], nSize);

    				printf("VPS=");
    				for(uint32_t i = 0; i < nSize + startCodeLen; ++i)
    				{
                        printf(" %02x", lpSPS[i]);
    				}
    				printf("\n");

    				bResult = sendVideoPacket(lpSPS, nSize, 0);
    				if(!bResult)
                    {
                        errorf("sendVideoPacket failed\n");
                        return false;
                    }

    				sendFlag = 1;
				}                
				free(vpsHeader[ix]);
			}
			free(vpsHeader);
			free(vpsSize);    
			*/

			///处理SPS信息
			sendFlag = 0;
			for(ix = 0; spsSize[ix] != 0; ++ix) 
			{
			    if(sendFlag == 0) // 只发送第一个SPS信息...
			    {
			        startCodeLen = sizeof(nalStartCode);
    				nSize = spsSize[ix];

    				memcpy(lpSPS, nalStartCode, startCodeLen);
    				memcpy(&lpSPS[startCodeLen], spsHeader[ix], nSize);

    				nSize += startCodeLen; //更新发送的数据长度

    				printf("SPS=");
    				for(uint32_t i = 0; i < nSize + startCodeLen; ++i)
    				{
                        printf(" %02x", lpSPS[i]);
    				}
    				printf("\n");

    				bResult = sendVideoPacket(lpSPS, nSize, 0);
    				if(!bResult)
                    {
                        errorf("sendVideoPacket failed\n");
                        return false;
                    }

    				sendFlag = 1;
				}                
				free(spsHeader[ix]);
			}
			free(spsHeader);
			free(spsSize);

			///处理PPS信息
			sendFlag = 0;
			for(ix = 0; ppsSize[ix] != 0; ++ix) // 只发送第一个PPS信息...
			{
			    if(sendFlag == 0)
			    {
    				nSize = ppsSize[ix];

                    startCodeLen = sizeof(nalStartCode);
    				memcpy(lpPPS, nalStartCode, startCodeLen);
    				memcpy(&lpPPS[startCodeLen], ppsHeader[ix], nSize);

    				printf("PPS=");
    				for(uint32_t i = 0; i < nSize + startCodeLen; ++i)
    				{
                        printf(" %02x", lpPPS[i]);
    				}
    				printf("\n");

    				nSize += startCodeLen; //更新发送的数据长度

    				bResult = sendVideoPacket(lpPPS, nSize, 0);
    				if(!bResult)
                    {
                        errorf("sendVideoPacket failed\n");
                        return false;
                    }

    				sendFlag = 1;
				}
				free(ppsHeader[ix]);
			}
			free(ppsHeader);
			free(ppsSize);
		} 
		else if( MP4_IS_AUDIO_TRACK_TYPE( type )
		    && ((m_playAbility == VIDEO_AUDIO) || (m_playAbility == AUDIO_ONLY))) 
		{			
			if( tidAudio > 0 )
				continue; // 音频已经有效，检测下一个...
			// 获取音频信息...
			tidAudio = id;
			printf("=== tidAudio:%d, type:%s ===\n", tidAudio, type);
			if(tidAudio == MP4_INVALID_TRACK_ID)
			{
                continue;
			}
			
			char * lpAudioInfo = MP4Info(inFile, id);
			printf("lpAudioInfo:%s \n", lpAudioInfo);
			free(lpAudioInfo);
 
			// 获取音频的类型/名称/采样率/声道信息...
			audio_type = MP4GetTrackAudioMpeg4Type(inFile, id);
			printf("===audio_type:%d ===\n", audio_type);
			audio_channel_num = MP4GetTrackAudioChannels(inFile, id);
			printf("===audio_channel_num:%d ===\n", audio_channel_num);
			const char * audio_name = MP4GetTrackMediaDataName(inFile, id);
			printf("===audio_encode:%s ===\n", audio_name);
			audio_time_scale = MP4GetTrackTimeScale(inFile, id);
			printf("===audio_time_scale:%u ===\n", audio_time_scale); //音频每秒刻度数(采样率)
			if (audio_time_scale == 48000)
				audio_rate_index = 0x03;
			else if (audio_time_scale == 44100)
				audio_rate_index = 0x04;
			else if (audio_time_scale == 32000)
				audio_rate_index = 0x05;
			else if (audio_time_scale == 24000)
				audio_rate_index = 0x06;
			else if (audio_time_scale == 22050)
				audio_rate_index = 0x07;
			else if (audio_time_scale == 16000)
				audio_rate_index = 0x08;
			else if (audio_time_scale == 12000)
				audio_rate_index = 0x09;
			else if (audio_time_scale == 11025)
				audio_rate_index = 0x0a;
			else if (audio_time_scale == 8000)
				audio_rate_index = 0x0b;
			printf("===audio_rate_index：%u ===\n", audio_rate_index);//音频采样率编号
			
       
            AudioDecFormat decFormat;
            memset(&decFormat, 0, sizeof(decFormat));
            decFormat.type = audioEncAAC;
            decFormat.frequency = audio_time_scale;
    	    decFormat.depth = 16; //todo  
    	    decFormat.channels = audio_channel_num;
            
            if((!m_pAudioDecode) || (!m_pAudioDecode->setFormat(&decFormat)))
            {
                errorf("AudioDecode setFormat error\n");
                return false;
            }
            
			if((!m_pAudioDecode) || (!m_pAudioDecode->start()))
            {
                errorf("AudioDecode start error\n");
                return false;
            }

            m_audioRateIndex = audio_rate_index;
            m_audioChannelNum = audio_channel_num;

            // 获取音频扩展信息...
            uint8_t  * pAES = NULL;
            uint32_t   nSize = 0;
            bool haveEs = MP4GetTrackESConfiguration(inFile, id, &pAES, &nSize);
			// 存储音频扩展信息...
			printf("=== Exten Size: %u ===\n", nSize);//音频扩展信息长度
            
			if( pAES != NULL ) 
			{
				free(pAES);
				pAES = NULL;
			}
		}
	}
	// 如果音频和视频都没有，返回失败...
	if((tidVideo == MP4_INVALID_TRACK_ID) && (tidAudio == MP4_INVALID_TRACK_ID)) 
	{
		errorf("parseMP4HeadInfo failed, trackid invaild\n");
		return false;
	}
	m_videoTrackId = tidVideo;
	m_audioTrackId = tidAudio;

	tracef("parseMP4HeadInfo success\n");
	return true;
}
 
// 抽取一帧音频或视频数据帧...
bool VideoFileStream::doMP4ReadOneFrame(MP4FileHandle inFile, MP4TrackId tid, uint32_t sid, bool bIsVideo, uint32_t & outSendTime)
{
    if(inFile == MP4_INVALID_FILE_HANDLE)
    {
        errorf("MP4FileHandle is invalid\n");
        return false;
    }
    
	uint8_t*	    pSampleData = NULL;		// 帧数据指针
	uint32_t		nSampleSize = 0;		// 帧数据长度
	MP4Timestamp	nStartTime = 0;			// 开始时间
	MP4Duration		nDuration = 0;			// 持续时间
	MP4Duration     nOffset = 0;			// 偏移时间
	bool			bIsKeyFrame = false;	// 是否关键帧
	uint32_t		timescale = 0;
	uint64_t		msectime = 0;
	char nalStartCode[4] = {0x00,0x00,0x00,0x01};
	uint32_t  startCodeLen = 0;

	uint8_t audioData[4096] = {0};
 
	// 读取轨道的采样率 和 当前帧的时间戳...
	timescale = MP4GetTrackTimeScale( inFile, tid );
	msectime = MP4GetSampleTime( inFile, tid, sid );
 
	// 读取一帧数据帧，失败，直接返回...
	if( false == MP4ReadSample(inFile, tid, sid, &pSampleData, &nSampleSize, &nStartTime, &nDuration, &nOffset, &bIsKeyFrame) )
		return false;

    
 
	// 计算当前读取数据帧的时间戳...
	// 计算发送时间 => PTS => 刻度时间转换成毫秒...
	msectime *= UINT64_C( 1000 );
	msectime /= timescale;
	// 计算开始时间 => DTS => 刻度时间转换成毫秒...
	nStartTime *= UINT64_C( 1000 );
	nStartTime /= timescale;
	// 计算偏差时间 => CTTS => 刻度时间转换成毫秒...
	nOffset *= UINT64_C( 1000 );
	nOffset /= timescale;

    if(nSampleSize > 0)
    {
    	if(bIsVideo)
        {
            startCodeLen = sizeof(nalStartCode);
            if(nSampleSize > startCodeLen)
            {
                memcpy(pSampleData, nalStartCode, startCodeLen);            
                sendVideoPacket(pSampleData, nSampleSize, msectime);
            }
        }
        else
        {
            //tracef("parseMP4 audio enter, nSampleSize:%d\n",nSampleSize);

        #if 1          
			uint8_t adtsHeader[7] = {0};
            adtsHeader[0] = 0xFF;
            adtsHeader[1] = 0xF1; //有的网站给的是F8，这里需要甄别

            int headLen = sizeof(adtsHeader)/sizeof(uint8_t);
            int profile = 2; //AAC LC, 后续计算的profile值等于Audio Object Type的值减1
            int freqIdx = m_audioRateIndex;///8000   44100对应的值是4
            int chanCfg = m_audioChannelNum; //mono channel
            int packetLen = nSampleSize + headLen; // - 4;//nSampleSize为rtp的payload数据


            adtsHeader[2] = ((profile -1 )<<6) + (freqIdx << 2) + (chanCfg >> 2);
            adtsHeader[3] = ((chanCfg & 3) << 6) + (packetLen >> 11);
            adtsHeader[4] = (packetLen & 0x7ff) >> 3;
            adtsHeader[5] = ((packetLen & 0x7) << 5)|0x1f;
            adtsHeader[6] = 0xFC;
            
            //Media_FILE_write("wyg/audiofile.aac", adtsHeader, sizeof(adtsHeader));

            //Media_FILE_write("wyg/audiofile.aac", (void*)(pSampleData), nSampleSize);


            memcpy(audioData, adtsHeader, headLen);
            memcpy(audioData + headLen, pSampleData, nSampleSize);

            sendAudioPacket(audioData, nSampleSize + headLen);
            
       #endif
            
        }
    }
 
	// 这里需要释放读取的缓冲区...
	MP4Free(pSampleData);
	pSampleData = NULL;
 
	// 返回发送时间(毫秒) => 已将刻度时间转换成了毫秒...
	outSendTime = (uint32_t)msectime;
	
	// 打印获取的音视频数据帧内容信息...
	//tracef("[%s] duration = %llu, offset = %llu, KeyFrame = %d, SendTime = %lu, StartTime = %llu, Size = %lu\n", bIsVideo ? "Video" : "Audio", nDuration, nOffset, bIsKeyFrame, outSendTime, nStartTime, nSampleSize);
 
	return true;
}

bool VideoFileStream::sendAudioPacket(unsigned char *buffer, int buflen)
{
    if(m_pAudioDecode)
    {
        m_audioPacket.buffer = buffer;
        m_audioPacket.length = buflen;
        
        return m_pAudioDecode->putPacket(&m_audioPacket);
    }

    return false;
}

void VideoFileStream::VideoThreadProc(Fdt::Thread *thread)
{
    thread->setPriority(Fdt::PRIORITY_NORMAL, Fdt::POLICY_RR);

    uint32_t dwOutVSendTime = 0;
	uint32_t iVSampleInx = 1;
	bool iRet = false;
	unsigned long long fps = 33; //ms
	unsigned long long ts = 0;
	
    
    while (thread->running())
    {
        if(m_videoTrackId == MP4_INVALID_TRACK_ID)
        {
            thread->sleep(5);
        }
        else
        {
        	if(m_videoTrackId != MP4_INVALID_TRACK_ID)
        	{
        	    ts = vdo_get_time();
        	    // 读取一帧视频帧...
        	    m_fileMutex.lock();
        	    iRet = doMP4ReadOneFrame(m_fileHd, m_videoTrackId, iVSampleInx++, true, dwOutVSendTime);
            	if(!iRet) 
            	{
            	    //printf("******************Frame read finish or error\n");
            	    iVSampleInx = 1;            
            	}
            	m_fileMutex.unlock();
        	}

        	unsigned long long  runtime = vdo_get_time() - ts;

            //printf("dealPacket runtime=%llu\n",runtime);
            if(runtime < fps)
            {
                thread->sleep(fps - runtime);
            }
        }
        
        
    }

    return;
}

void VideoFileStream::AudioThreadProc(Fdt::Thread *thread)
{
    thread->setPriority(Fdt::PRIORITY_NORMAL, Fdt::POLICY_RR);

	uint32_t dwOutASendTime = 0;
	uint32_t iASampleInx = 1;
	bool iRet = false;
	unsigned long long fps = 29; //考虑alsa音频输出对时间要求比较高,超过29ms会导致包undenrun错误
	unsigned long long ts = 0;
	    
    while (thread->running())
    {
        if(m_audioTrackId == MP4_INVALID_TRACK_ID)
        {
            thread->sleep(5);
        }
        else
        {
        	if(m_audioTrackId != MP4_INVALID_TRACK_ID)
        	{
        	    ts = vdo_get_time();
        	    
            	// 读取一帧音频帧...
            	m_fileMutex.lock();
            	iRet = doMP4ReadOneFrame(m_fileHd, m_audioTrackId, iASampleInx++, false, dwOutASendTime);
            	if(!iRet) 
            	{
                    iASampleInx = 1;          
            	}
            	m_fileMutex.unlock();
        	}

        	unsigned long long  runtime = vdo_get_time() - ts;

            //printf("dealPacket runtime=%llu\n",runtime);
            if(runtime < fps)
            {
                thread->sleep(fps - runtime);
            }
        }
        
    }

    return;
}

}



